-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Various bug fixes. #163
Various bug fixes. #163
Conversation
/// The generated key is a 32-character string that contains alphanumeric characters | ||
/// as well as symbols from the set: .=\-:[_@\*]+? | ||
/// </summary> | ||
public Unclassified32CharacterString() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Problem #1 in the strange test code paths Ross found suspicious: the test data generated by this rule was not in strict conformance across all its logic.
Basically, we have no general testing of the UnclassifiedPotentialSecurityKeys
patterns list, which I could go slam into some other bucketing test. I am deferring that work for now.
For now, I'm ripping this rule out because it's noisy and not useful. The intent here was to capture an ancient variation of an generated AAD SPN secret, then the rule was renamed as a generic Unclassified32CharacterString
because it fired too frequently and really it should have been deleted entirely. If there's an analysis here, it will argue its way back in.
@@ -22,7 +22,7 @@ public Unclassified16ByteHexadecimalString() | |||
DetectionMetadata = DetectionMetadata.HighEntropy | DetectionMetadata.Unclassified | DetectionMetadata.LowConfidence; | |||
} | |||
|
|||
public override Tuple<string, string>? GetMatchIdAndName(string match) => new Tuple<string, string>("SEC000/001", "Unclassified64ByteBase64String"); | |||
public override Tuple<string, string>? GetMatchIdAndName(string match) => new Tuple<string, string>("SEC000/002", nameof(Unclassified16ByteHexadecimalString)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
boo. cut and paste error. I added invariant tests that ensure no rules collide with each other in core metadata expression and also that GetMatchIdAndName
results always match the declared rule id and name.
This is what we get for being 'cute' and allowing a single rule to be flexible in what ids it covers. That's useful but it tortures the design more than a little and leaves us vulnerable to bugs of this kind.
@@ -5,14 +5,14 @@ | |||
|
|||
namespace Microsoft.Security.Utilities | |||
{ | |||
public class AdoPat : RegexPattern | |||
public class AdoLegacyPat : RegexPattern |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public class AdoLegacyPatTests | ||
{ | ||
[TestMethod] | ||
public void AdoLegacyPat_InvalidBase32Input() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Boo. For all my talk of test-driven development in the last PR, I ended up not shipping a correct fix! The reason? Late-breaking changes to a test I authored altered the code coverage and then at the very end a conversion from generated exception type (from FormatException
to ArgumentException
) left the code in a crashing condition,
Sigh. Even while waiting on a good unit test pattern, I should have started with simple, atomic tests to reveal bugs and prove they are fixed. Duly chastened, I return here with this review. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sigh. Even while waiting on a good unit test pattern, I should have started with simple, atomic tests to reveal bugs and prove they are fixed. Duly chastened, I return here with this review.
❤️
src/Tests.Microsoft.Security.Utilities.Core/WellKnownRegexPatternsTests.cs
Show resolved
Hide resolved
src/Microsoft.Security.Utilities.Core/WellKnownRegexPatterns.cs
Outdated
Show resolved
Hide resolved
src/Tests.Microsoft.Security.Utilities.Core/WellKnownRegexPatternsTests.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The invariant test for no duplicate ids looks like it's missing a key piece. See comments.
Otherwise, just some nits.
public class AdoLegacyPatTests | ||
{ | ||
[TestMethod] | ||
public void AdoLegacyPat_InvalidBase32Input() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sigh. Even while waiting on a good unit test pattern, I should have started with simple, atomic tests to reveal bugs and prove they are fixed. Duly chastened, I return here with this review.
❤️
var classifier = new AdoLegacyPat(); | ||
string invalidInput = "=22222222222222222222222222"; | ||
var result = classifier.GetMatchIdAndName(invalidInput); | ||
Assert.IsNull(result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yay! This is a very easy to understand test <3.
For due diligence, if you have not already, run this test attached with a debugger and ensure the null
is resulting from the expected error line getting hit. i.e. this is null
for a particular reason, and not a general indeterminate reason.
Depending on perf constraints, there's more formal methods to handle this kinda testing — e.g. internal logging and asserting on specific logs are hit even if everything publicly is turned into a null
. Out of scope for this PR, of course, but food for thought. #Resolved
src/Tests.Microsoft.Security.Utilities.Core/WellKnownRegexPatternsTests.cs
Outdated
Show resolved
Hide resolved
HashSet<string> ruleIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | ||
HashSet<string> ruleNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HashSet<string> ruleIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | |
HashSet<string> ruleNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | |
HashSet<string> seenRuleIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | |
HashSet<string> seenRuleNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); |
nit: these renames will make the loop code easier to read #Resolved
src/Tests.Microsoft.Security.Utilities.Core/WellKnownRegexPatternsTests.cs
Outdated
Show resolved
Hide resolved
result = ruleIds.Contains(pattern.Id); | ||
result.Should().BeFalse(because: $"Pattern '{pattern.GetType().Name}' should not share its Id with another rule: '{pattern.Id}'"); | ||
|
||
result = ruleNames.Contains(pattern.Name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
result = ruleNames.Contains(pattern.Name); | |
bool duplicatePatternName = ruleNames.Contains(pattern.Name); |
nit: instead of using generic result
everywhere some more specific names will increase readability. #WontFix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't do this. I set result to my test condition so that it's on one line. Then I follow it with an assertion with a strong readable message.
result
isn't intended to encode something useful, the detailed, highly authored message it.
i could allocate new variables with names but this tends to consume more space and i worry about readability.
let's punt for now i will think about your suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read the code top to bottom. A well-named variable means I know how to read/check the line without the out-of-order flow.
Better yet if you want the because
to be the sole communicator, skip using the variable altogether:
alreadySeeanRullName.Should().Not().Contains(pattern.Name, because: "…");
These are nits, so keep thinking on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a good suggestion to encode more information about the test expectation in the variable name, I will keep this in mind moving forward.
Dismissing as not to block as bug is in unrelated test code.
…curity-utilities into 1-16-0-back-to-main
SEC000/101.Unclassified32CharacterString
as noisy and not useful.SEC101/102.AdoPat
friendly name toAdoLegacyPat
.SEC000/002.Unclassified16ByteHexadecimalString
id and rule name on callingGetMatchIdAndName
(whereSEC000/001.Unclassified64ByteBase64String
was returned incorrectly before).System.FormatException: The input is not a valid Base-46 string
errors callingSEC101/102.AdoPat.GetMatchIdAndName
by swallowing correct exception kindArgumentException
inIsChecksumValid
helper.I wish we could all be in a room together where I would invite everyone to hurl coffee cups, peanut shells, or anything else handy at my head. I completely failed to test all the code paths I strongly asserted I was covering in the last PR. Furthermore, Ross' confusion at a very strange set of conditions in a test I authored proved to be entirely warranted: these conditions indicate other bugs I simply wasn't seeing.
I am only human.
Festina lente
. I should have it tattooed in reverse on my forehead so that it's the first thing I see in the mirror every morning.